export * from './decorator'

import redis from 'redis'
import fs from 'fs'
import path from 'path'
import { promisify } from 'util'
import { getMeta, instance2Object } from './decorator'

const CONFIG_FILE_PATH = path.resolve(__dirname, '../../../redisconfig.json')

let options: redis.ClientOpts = {
  host: '127.0.0.1',
  port: 6379
}

if (fs.existsSync(CONFIG_FILE_PATH)) {
  try {
    options = JSON.parse(fs.readFileSync(CONFIG_FILE_PATH, { encoding: 'utf-8' })) as redis.ClientOpts
  } catch (error) {
    console.log(error)
  }
} else {
  // console.log('redisconfig.json 配置文件不存在，将使用默认配置')
}

// 判断是否有相关的环境变量
if (process.env.REDIS_HOST) {
  options.host = process.env.REDIS_HOST
}

if (process.env.REDIS_PORT) {
  options.port = Number(process.env.REDIS_PORT)
}

if (process.env.REDIS_AUTH_PASS) {
  options.auth_pass = process.env.REDIS_AUTH_PASS
}

let client: redis.RedisClient

// 是否连接 redis
if (process.env.CONNECT_REDIS && String(process.env.CONNECT_REDIS) === 'true') {
  client = redis.createClient(options)
}

client && client.on('ready', function() {
  // console.log('redis 连接成功')
  Redis.isReady = true
})

client && client.on('error', function(error) {
  console.log(error)
  Redis.isReady = false
})

// interface ObjectLiteral {
//   [key: string]: any;
// }

// type ObjectType<T> = {
//   new (): T;
// } | Function

// 设置 hashmap
interface HmgetHandler {
  <T extends Object>(key: string, constructor: new () => T): Promise<T>
  (key: string): Promise<(string | number)[]>
}

class Redis {
  static isReady = false

  static client = client

  // 删除
  // static del = promisify(client.del).bind(client) as (key: string) => Promise<string>
  // 删除
  static del = async (key: string) => {
    if (!client) throw new Error('redis 尚未连接')

    return await promisify(client.del).bind(client)(key) as string
  }

  // 字符串
  // static set = promisify(client.set).bind(client) as (key: string, value: string | number) => Promise<string>
  static set = async (key: string, value: string | number) => {
    if (!client) throw new Error('redis 尚未连接')

    return await promisify(client.set).bind(client)(key, value) as string
  }
  // static get = promisify(client.get).bind(client) as (key: string) => Promise<string>
  static get = async (key: string) => {
    if (!client) throw new Error('redis 尚未连接')

    return await promisify(client.get).bind(client)(key) as string
  }

  // hashmap
  static hmset = (key: string, arg1: { [key: string]: string | number | any }) => {
    if (!client) throw new Error('redis 尚未连接')

    return new Promise((resolve, reject) => {
      const arg = instance2Object(arg1)
      client.hmset(key, arg, (err, val) => {
        err ? reject(err) : resolve(val)
      })
    })
  }
  static hmget: HmgetHandler = (key: string, constructor?: any) => {
    if (!client) throw new Error('redis 尚未连接')

    const meta = constructor ? getMeta(constructor) : null

    return new Promise((resolve: (any) => void, reject) => {
      client.hmget(key, meta ? meta.keys : null, (err, vals) => {
        if (err) {
          reject(err)
        } else {
          if (constructor) {
            const obj = {}
            meta.keys.forEach((key, index) => {
              const MetaData = meta.MetaDatas[key]
              if (MetaData && vals[index] !== null) {
                obj[key] = MetaData(vals[index])
              } else {
                obj[key] = vals[index]
              }
            })
            resolve(obj)
          } else {
            resolve(vals)
          }
        }
      })
    })
  }

  // 数组
  static L_META_DATA_KEY = 'L_META_DATA_KEY'
  // static lpush = promisify(client.lpush).bind(client) as (key: string, arg1: string | string[]) => Promise<number>
  static lpush = async (key: string, arg1: string | string[]) => {
    if (!client) throw new Error('redis 尚未连接')

    return await promisify(client.lpush).bind(client)(key, arg1) as number
  }
  // static lrange = promisify(client.lrange).bind(client) as (key: string, start: number, stop: number) => Promise<string[]>
  static lrange = async (key: string, start: number, stop: number) => {
    if (!client) throw new Error('redis 尚未连接')

    return await promisify(client.lrange).bind(client)(key, start, stop) as string[]
  }

  static pushList = (key: string, list: (string | number)[], isSaveMeta = false) => {
    if (!client) throw new Error('redis 尚未连接')

    return Promise.all([
      Redis.lpush(key, list.map(item => String(item))),
      isSaveMeta && Redis.lpush(key + Redis.L_META_DATA_KEY, list.map(item => typeof item)),
    ])
  }
  static setList = (key: string, list: (string | number)[], isSaveMeta = false) => {
    if (!client) throw new Error('redis 尚未连接')

    client.del(key)
    client.del(key + Redis.L_META_DATA_KEY)
    return Redis.pushList(key, list, isSaveMeta)
  }

  static getList = (key: string) => {
    if (!client) throw new Error('redis 尚未连接')

    return new Promise((resolve: (list: (string | number)[]) => void, reject) => {
      Promise.all([
        Redis.lrange(key, 0, -1),
        Redis.lrange(key + Redis.L_META_DATA_KEY, 0, -1),
      ]).then(([list, metaList]) => {
        const reverseListmMetaList = metaList.reverse() 
        const result = list.reverse().map((item, index) => {
          try {
            if (reverseListmMetaList[index] === 'number') {
              return Number(item)
            }
          } catch (error) {
            console.log('redis 数组类型转换错误');
          }
          return item
        })

        resolve(result)
      }).catch(reject)
    })
  }
}

export default Redis